package cn.mw.cmdb.service.impl;

import cn.mw.cmdb.entity.InstanceImportHistoryInfo;
import cn.mw.cmdb.entity.InstanceImportHistoryStatusCountInfo;
import cn.mw.cmdb.entity.InstanceImportResultInfo;
import cn.mw.cmdb.mongoMapper.InstanceImportHistoryInfoMapper;
import cn.mw.cmdb.mongoMapper.InstanceImportResultInfoMapper;
import cn.mw.cmdb.service.InstanceImportHistoryService;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.util.ExcelUtils;
import cn.mw.cmdb.view.ViewConvertContext;
import cn.mw.cmdb.view.ViewManager;
import cn.mw.components.mongodb.entity.*;
import cn.mw.components.mongodb.exception.BusinessException;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.microMonitorCommon.constant.Constants;
import cn.mw.microMonitorCommon.security.dto.MwUser;
import cn.mw.microMonitorCommon.security.utils.SecurityUtils;
import cn.mw.microMonitorCommon.utils.DatabaseUtils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.mongodb.client.gridfs.model.GridFSFile;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
public class InstanceImportHistoryServiceImpl extends BaseSearchService<InstanceImportHistoryInfo> implements InstanceImportHistoryService {

    @Autowired
    private InstanceImportHistoryInfoMapper instanceImportHistoryInfoMapper;

    @Autowired
    private InstanceImportResultInfoMapper instanceImportResultInfoMapper;

    @Autowired
    private ViewManager viewManager;

    @Autowired
    private GridFsTemplate gridFsTemplate;

    @Override
    protected InstanceImportHistoryInfo genObject() {
        return new InstanceImportHistoryInfo();
    }

    @Override
    public boolean saveInstanceImportHistory(InstanceImportHistoryInfo instanceImportHistoryInfo) {
        instanceImportHistoryInfoMapper.save(instanceImportHistoryInfo);
        return true;
    }

    @Override
    public boolean batchSaveInstanceImportResult(List<InstanceImportResultInfo> instanceImportResultInfoList) {
        if (CollectionUtils.isNotEmpty(instanceImportResultInfoList)){
            instanceImportResultInfoMapper.insertAll(instanceImportResultInfoList);
        }
        return true;
    }

    @Override
    public Object getInstanceImportHistoryInfoByPage(SearchParam param) throws Exception {
        param.setSearchClass(InstanceImportHistoryInfo.class);
        if (StringUtils.isEmpty(param.getKeywordValue()) && StringUtils.equals(param.getOperation(), CriteriaOpsType.keyword.name())) {
            param.setOperation(null);
        }
        // 管理员可以查询所有导入导出记录 其它用户只能查看自己的导入导出记录
        MwUser mwUser = SecurityUtils.getUser();
        if (!mwUser.isAdmin()) {
            param.addSubCondition(CMDBTool.getFieldName(InstanceImportHistoryInfo::getOperator)
                    , new SubCondition(CriteriaOpsType.is.name(), mwUser.getNickName()));
        }

        Criteria criteria = genCriteria(param);
        if (StringUtils.isEmpty(param.getSortName())) {
            // 默认按照上报事件倒序排列
            param.setSortName(DatabaseUtils.getFieldName(InstanceImportHistoryInfo::getOperateDate));
            param.setSortOrder("DESC");
        }
        // 设置引擎类型名称及引擎名称
        List<InstanceImportHistoryInfo> resultList = CMDBTool.selectPageList(instanceImportHistoryInfoMapper, criteria, param);
        ViewConvertContext context = new ViewConvertContext();
        return viewManager.getListViewObject(resultList, InstanceImportHistoryInfo.class, param, context);
    }

    @Override
    public Object getInstanceImportDetailByPage(SearchParam param) throws Exception {
        param.setSearchClass(InstanceImportResultInfo.class);
        Criteria criteria = genCriteria(param);
        if (StringUtils.isEmpty(param.getSortName())) {
            // 默认按照上报事件倒序排列
            param.setSortName(DatabaseUtils.getFieldName(InstanceImportResultInfo::getRowNo));
            param.setSortOrder("ASC");
        }
        // 设置引擎类型名称及引擎名称
        List<InstanceImportResultInfo> resultList = CMDBTool.selectPageListByAggregate(InstanceImportResultInfo.class, instanceImportResultInfoMapper, param, criteria);
        ViewConvertContext context = new ViewConvertContext();
        return viewManager.getListViewObject(resultList, InstanceImportResultInfo.class, param, context);
    }

    private int extractNumber(String str) {
        if (StringUtils.isEmpty(str)) {
            return 0;
        }
        return Integer.parseInt(str.replaceAll("\\D+", ""));
    }

    @Override
    public Map<String, Integer> getInstanceImportHistoryInfoStatusCount(SearchParam param) throws Exception {
        param.setSearchClass(InstanceImportHistoryInfo.class);
        // 管理员可以查询所有导入导出记录 其它用户只能查看自己的导入导出记录
        MwUser mwUser = SecurityUtils.getUser();
        if (!mwUser.isAdmin()) {
            param.addSubCondition(CMDBTool.getFieldName(InstanceImportHistoryInfo::getOperator)
                    , new SubCondition(CriteriaOpsType.is.name(), mwUser.getNickName()));
        }
        if (CriteriaOpsType.keyword.name().equals(param.getOperation()) && StringUtils.isEmpty(param.getKeywordValue())) {
            param.setOperation(null);
        }
        Criteria criteria = genCriteria(param);
        param.setPage(null);
        GroupSearchOperation groupSearchOperation = new GroupSearchOperation();
        String groupName = CMDBTool.getFieldName(InstanceImportHistoryInfo::getOperateResult);
        groupSearchOperation.addGroupField(CMDBTool.getFieldName(InstanceImportHistoryInfo::getOperateResult), groupName);
        groupSearchOperation.addCalculateField(CMDBTool.getFieldName(InstanceImportHistoryStatusCountInfo::getCount), GroupCalculateMethodEnum.count, groupName);
        List<InstanceImportHistoryStatusCountInfo> result = CMDBTool.selectCountByAggregate(InstanceImportHistoryStatusCountInfo.class, instanceImportHistoryInfoMapper, criteria, groupSearchOperation);
        Map<String, Integer> status2CountMap = new LinkedHashMap<>();
        status2CountMap.put(Constants.OperationStatus.SUCCESS, 0);
        status2CountMap.put(Constants.OperationStatus.PARTIALLY_SUCCESS, 0);
        status2CountMap.put(Constants.OperationStatus.FAILED, 0);
        if (CollectionUtils.isNotEmpty(result)) {
            Map<String, Integer> queryMap = result.stream().collect(Collectors.toMap(InstanceImportHistoryStatusCountInfo::getId, InstanceImportHistoryStatusCountInfo::getCount));
            for (Map.Entry<String, Integer> item : queryMap.entrySet()) {
                if (status2CountMap.containsKey(item.getKey())) {
                    status2CountMap.put(item.getKey(), status2CountMap.get(item.getKey()) + item.getValue());
                } else {
                    status2CountMap.put(item.getKey(), item.getValue());
                }
            }
        }
        return status2CountMap;
    }

    public List<InstanceImportResultInfo> getExportResultList(SearchParam param) throws Exception {
        param.setSearchClass(InstanceImportResultInfo.class);
        Criteria criteria = genCriteria(param);
        if (StringUtils.isEmpty(param.getSortName())) {
            // 默认按照上报事件倒序排列
            param.setSortName(DatabaseUtils.getFieldName(InstanceImportResultInfo::getOperateDate));
            param.setSortOrder("DESC");
        }
        // 查询导出清单
        List<InstanceImportResultInfo> resultList = CMDBTool.selectPageList(instanceImportResultInfoMapper, criteria, param);
        resultList.forEach(item -> {
            item.setResultMsg(item.getResult() ? Constants.OperationStatus.SUCCESS : Constants.OperationStatus.FAILED);
        });
        // 导出清单排序
        Collections.sort(resultList, (o1, o2) -> Integer.compare(extractNumber(o1.getRowIndex()), extractNumber(o2.getRowIndex())));
        return resultList;
    }

    @Override
    public void export(SearchParam param, HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<InstanceImportResultInfo> resultList = getExportResultList(param);
        ExcelWriter excelWriter = null;
        String fileName = String.valueOf(System.currentTimeMillis());

        List<String> includeColumnFiledNames = new ArrayList<>();
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getInstanceName));
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getModelName));
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getFinanceParentModelName));
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getRowIndex));
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getOperateType));
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getResultMsg));
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getOperator));
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getOperateDate));
        includeColumnFiledNames.add(CMDBTool.getFieldName(InstanceImportResultInfo::getErrorMsg));
        try {
            excelWriter = ExcelUtils.getExcelWriter(fileName, response, InstanceImportResultInfo.class);
            WriteSheet sheet = EasyExcel.writerSheet(0, "sheet" + 0)
                    .includeColumnFieldNames(includeColumnFiledNames)
                    .build();
            excelWriter.write(resultList, sheet);
        } catch (IOException e) {
            log.error("资产导入结果导出失败:", e);
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    @Override
    public void exportExcelImportResult(SearchParam param, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String historyId = ((List) param.getCondition().get(DatabaseUtils.getFieldName(InstanceImportResultInfo::getHistoryId)).getValue()).get(0).toString();
        InstanceImportHistoryInfo instanceImportHistoryInfo = instanceImportHistoryInfoMapper.findOne(
                new Query(Criteria.where("_" + DatabaseUtils.getFieldName(InstanceImportHistoryInfo::getId)).is(historyId)));
        if (instanceImportHistoryInfo != null && instanceImportHistoryInfo.getFileId() != null) {
            GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(instanceImportHistoryInfo.getFileId())));
            if (gridFSFile == null) {
                throw new BusinessException("File not found");
            }
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename=" + System.currentTimeMillis() + ".xls");
            insertColumn(param, gridFSFile, 0, "失败原因", response.getOutputStream());
        } else {
            export(param, request, response);
        }
    }

    @Override
    public void updateRollBackFlag(List<String> historyIds) throws Exception {
        Query hisQuery = new Query(CriteriaWrapper.where(InstanceImportHistoryInfo::getId).in(historyIds));
        //根据map字段产生更新信息
        Update rollBackUpdate = new Update();
        rollBackUpdate.set(DatabaseUtils.getFieldName(InstanceImportHistoryInfo::getIsRollBack), true);
        instanceImportHistoryInfoMapper.updateMulti(hisQuery, rollBackUpdate);
    }

    public void insertColumn(SearchParam param, GridFSFile gridFSFile, int columnIndex, String newColumnHeader, OutputStream outputStream) throws Exception {
        InputStream inputStream = gridFsTemplate.getResource(gridFSFile).getInputStream();
        Workbook workbook;
        String fileName = gridFSFile.getFilename();
        if (fileName.endsWith(".xlsx")) {
            workbook = new XSSFWorkbook(new ByteArrayInputStream(inputStream.readAllBytes()));
        } else if (fileName.endsWith(".xls")) {
            workbook = new HSSFWorkbook(new ByteArrayInputStream(inputStream.readAllBytes()));
        } else {
            return;
        }

        Sheet sheet = workbook.getSheetAt(0); // 选择第一个 sheet
        int lastRowNum = sheet.getLastRowNum();

        // 获取校验结果
        List<InstanceImportResultInfo> instanceImportResultInfoList = getExportResultList(param);
        Map<Integer, String> row2MessageMap = new HashMap<>();
        for (InstanceImportResultInfo instanceImportResultInfo : instanceImportResultInfoList) {
            int rowIndex = extractNumber(instanceImportResultInfo.getRowIndex());
            if (rowIndex > 0) {
                row2MessageMap.put(rowIndex - 1, instanceImportResultInfo.getErrorMsg());
            }
        }
        List<Integer> needDeleteRows = new ArrayList<>();
        boolean isNeedCreateCol = sheet.getRow(0).getCell(0) != null && !StringUtils.equals(sheet.getRow(0).getCell(0).getStringCellValue(), newColumnHeader);
        // 插入数据列
        for (int i = 0; i <= lastRowNum; i++) {
            Row row = sheet.getRow(i);
            if (row == null) {
                row = sheet.createRow(i);
            }
            // 如果错误信息列已经存在则不创建
            if (isNeedCreateCol) {
                // 将原列及其右侧的列向右移动一列
                for (int j = row.getLastCellNum(); j > columnIndex; j--) {
                    Cell oldCell = row.getCell(j - 1);
                    if (oldCell != null) {
                        Cell newCell = row.createCell(j);
                        newCell.setCellValue(oldCell.toString());
                    }
                }
            }
            // 设置新列的值（此处设置为示例数据）
            Cell newCell = isNeedCreateCol ? row.createCell(columnIndex) : row.getCell(columnIndex);
            if (i == 0) {
                // 插入列标题
                newCell.setCellValue(newColumnHeader);
            } else if (row2MessageMap.containsKey(i)) {
                newCell.setCellValue(isNeedCreateCol ? modifyColumn(row2MessageMap.get(i)) : row2MessageMap.get(i));
            } else {
                needDeleteRows.add(i);
            }
        }
        for (int i = needDeleteRows.size() - 1; i > -1; i--) {
            deleteRow(sheet, needDeleteRows.get(i));
        }
        workbook.write(outputStream);
    }

    private void deleteRow(Sheet sheet, int rowIndex) {
        if (sheet instanceof HSSFSheet) {
            int lastRowNum = sheet.getLastRowNum();
            if (rowIndex >= 0 && rowIndex < lastRowNum) {
                sheet.shiftRows(rowIndex + 1, lastRowNum, -1);
            }
            if (rowIndex == lastRowNum) {
                HSSFRow removingRow = (HSSFRow) sheet.getRow(rowIndex);
                if (removingRow != null) {
                    sheet.removeRow(removingRow);
                }
            }
        } else if (sheet instanceof XSSFSheet) {
            int lastRowNum = sheet.getLastRowNum();
            if (rowIndex >= 0 && rowIndex < lastRowNum) {
                sheet.shiftRows(rowIndex + 1, lastRowNum, -1);
            }
            if (rowIndex == lastRowNum) {
                XSSFRow removingRow = (XSSFRow) sheet.getRow(rowIndex);
                if (removingRow != null) {
                    sheet.removeRow(removingRow);
                }
            }
        }
    }

    public String modifyColumn(String text) {
        if (StringUtils.isEmpty(text)) {
            return text;
        }
        // 正则表达式：提取 "第" 后面的数字
        Pattern pattern = Pattern.compile("(第)(\\d+)(列)");
        Matcher matcher = pattern.matcher(text);

        // 替换字符串中的数字部分
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            int columnNumber = Integer.parseInt(matcher.group(2));
            int newColumnNumber = columnNumber + 1;
            matcher.appendReplacement(result, matcher.group(1) + newColumnNumber + matcher.group(3));
        }
        matcher.appendTail(result);

        return result.toString();
    }


}
